﻿      // This is the Spanner runtime environment.
      // NOTE: This runtime assumes Knockout is available (http://www.knockoutjs.com).
      //
      var spanner = new (function () {

          // ---- Spanner code below. ----

          var self = this;

          // Set the ith item of an observable array.
          //
          self.setObsIth = function (obs, i, x) {
              var xs = obs();
              xs[i] = x;
              obs(xs);
          };

          // Set a field of an observable object.
          //
          self.setObsField = function (obs, setF, x) {
              var obj = obs();
              setF(obj, x);
              obs(obj);
          };

          self.spliceObsArray = function (obs, begin, howMany, ys) {
              var xs = obs();
              [].splice.apply(xs, [begin, howMany].concat(ys));
              obs(xs);
          };

          self.unshiftObsArray = function (obs, ys) {
              var xs = obs();
              [].unshift.apply(xs, ys);
              obs(xs);
          };

          self.replaceObsString = function (obs, pattern, replacement, flags) {
              var x = obs();
              x.replace(pattern, replacement, flags);
              obs(x);
          };

          // JavaScript's "(almost) any type can be extended with properties" scheme doesn't fit
          // C#, so in those cases we need to write "type adaptors".
          //
          self.regexExec = function (re, str) {
              var result = re.exec(str);
              if (!result) return result;
              var matchedSubstr = result[0];
              var parenMatches = [];
              for (var i = 1; i < result.length; i++) parenMatches.push(result[i]);
              // This return value must match class UI.RegexMatch.
              return {
                  MatchedSubstr: matchedSubstr,
                  ParenMatches: parenMatches,
                  MatchIndex: result.index,
                  MatchInput: result.input
              };
          }

          // Set up Knockout extensions for our primitive number types.
          // We need to ensure these are assigned numeric values when
          // receiving input from an input field.

          // An obsInt is always an integer or NaN.
          // String assignments are coerced to numbers.
          //
          self.obsInt = function (initialValue) {
              var obs = ko.observable(initialValue);
              var obsInt = ko.computed({
                  read: obs,
                  write: function (x) {
                      var num = +x;
                      obs(num === Math.floor(num) ? num : NaN);
                  }
              });
              obsInt(obs());
              return obsInt;
          };

          // An obsDouble is always a number or NaN.
          // String assignments are coerced to numbers.
          //
          self.obsDouble = function (initialValue) {
              var obs = ko.observable(initialValue);
              var obsDouble = ko.computed({
                  read: obs,
                  write: function (x) {
                      var num = +x;
                      obs(num);
                  }
              });
              obsDouble(obs());
              return obsDouble;
          };

          // Add a function to the window.onload chain.
          //
          self.windowOnLoad = function (f) {
              window.onload = (function (wol) {
                  if (wol) wol();
                  if (f) f();
              })(window.onload);
          };

          // Construct the argument for a Knockout template binding.
          //
          self.templateArgs = function (t) {
              return (t ? { name: t.__templateID, data: t, afterRender: t.__afterRender } : { if: false });
          };

          // All done.

          return self;

      })();
